﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace SimplexEngine
{
    internal class ShapeResourceLoader : IResourceLoader
    {        
        public List<String> GetSupportFormats()
        {
            List<String> support_formats = new List<string>();
            support_formats.Add(".SHAPE");
            return support_formats;
        }

        public ResourceType GetResourceType()
        {
            return ResourceType.STATIC_MESH;
        }

        public Object LoadResource(string path)
        {
            String shape_name = System.IO.Path.GetFileNameWithoutExtension(path).ToUpper();
            vertices.Clear();
            indices.Clear();

            switch (shape_name)
            { 
                case "CUBE":
                    CreateCube();
                    break;
                case "SPHERE":
                    CreateSphere();
                    break;
                case "PLANE":
                    CreatePlane();
                    break;
            }

            return CreateMesh();
        }

        private StaticMesh CreateMesh()
        {
            StaticMesh mesh = new StaticMesh();

            StaticMeshPart mesh_part = new StaticMeshPart();
            mesh_part.Material = new MRTBasicMaterial();

            MeshBuffer mesh_buffer = new MeshBuffer();
            mesh_buffer.SetVertices(vertices.ToArray());
            mesh_buffer.SetIndices(indices.ToArray());
            mesh_part.SetMeshBuffer(mesh_buffer);

            mesh.AddMeshPart(mesh_part);
            return mesh;
        }

        private List<Vertex> vertices = new List<Vertex>();
        private List<Int32> indices = new List<Int32>();

        private void AddVertex(Vector3 position, Vector3 normal, Vector2 texture_coordinate)
        {            
            Vertex vertex = new Vertex();
            vertex.Position = position;
            vertex.Normal = normal;
            vertex.TextureCoordinate0 = texture_coordinate;
            vertices.Add(vertex);
        }

        private void CreatePlane()
        {
            CreatePlaneByNormal(new Vector3(0, 0, 1));
        }

        protected void CreateCube()
        {
            Vector3[] normals = {new Vector3(0, 0, 1), new Vector3(0, 0, -1), new Vector3(1, 0, 0), new Vector3(-1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, -1, 0)};

            foreach (Vector3 normal in normals)
                CreatePlaneByNormal(normal);
        }

        protected void CreatePlaneByNormal(Vector3 normal)
        {
            Vector3 side1 = new Vector3(normal.Y, normal.Z, normal.X);
            Vector3 side2 = Vector3.Cross(normal, side1);

            indices.Add(vertices.Count + 0);
            indices.Add(vertices.Count + 1);
            indices.Add(vertices.Count + 2);

            indices.Add(vertices.Count + 0);
            indices.Add(vertices.Count + 2);
            indices.Add(vertices.Count + 3);

            AddVertex((normal - side1 - side2) * 0.5f, normal, new Vector2(1.0f, 1.0f));
            AddVertex((normal - side1 + side2) * 0.5f, normal, new Vector2(0.0f, 1.0f));
            AddVertex((normal + side1 + side2) * 0.5f, normal, new Vector2(0.0f, 0.0f));
            AddVertex((normal + side1 - side2) * 0.5f, normal, new Vector2(1.0f, 0.0f));        
        }

        protected void CreateSphere()
        {
            // codes below is come form xna creator club's example

            int tessellation = 16;

            int vertical_segments = tessellation;
            int horizontal_segments = tessellation * 2;

            float radius = 0.5f;

            AddVertex(Vector3.Down * radius, Vector3.Down, new Vector2(0.0f));

            for (int i = 0; i < vertical_segments - 1; ++i)
            {
                float latitude = ((i + 1) * MathHelper.Pi / vertical_segments) - MathHelper.PiOver2;

                float dy = (float)Math.Sin(latitude);
                float dxz = (float)Math.Cos(latitude);

                for (int j = 0; j < horizontal_segments; j++)
                {
                    float longitude = j * MathHelper.TwoPi / horizontal_segments;

                    float dx = (float)Math.Cos(longitude) * dxz;
                    float dz = (float)Math.Sin(longitude) * dxz;

                    Vector3 normal = new Vector3(dx, dy, dz);

                    AddVertex(normal * radius, normal, new Vector2(0.0f));
                }
            }

            AddVertex(Vector3.Up * radius, Vector3.Up, new Vector2(0.0f));

            for (int i = 0; i < horizontal_segments; i++)
            {
                indices.Add(0);
                indices.Add(1 + (i + 1) % horizontal_segments);
                indices.Add(1 + i);
            }

            for (int i = 0; i < vertical_segments - 2; i++)
            {
                for (int j = 0; j < horizontal_segments; j++)
                {
                    int nextI = i + 1;
                    int nextJ = (j + 1) % horizontal_segments;

                    indices.Add(1 + i * horizontal_segments + j);
                    indices.Add(1 + i * horizontal_segments + nextJ);
                    indices.Add(1 + nextI * horizontal_segments + j);

                    indices.Add(1 + i * horizontal_segments + nextJ);
                    indices.Add(1 + nextI * horizontal_segments + nextJ);
                    indices.Add(1 + nextI * horizontal_segments + j);
                }
            }

            for (int i = 0; i < horizontal_segments; i++)
            {
                indices.Add(vertices.Count - 1);
                indices.Add(vertices.Count - 2 - (i + 1) % horizontal_segments);
                indices.Add(vertices.Count - 2 - i);
            }           
        }
    }
}
